#include "Landscape.h"
#include "../Maths/Math.h"
#include "../Components/Renderer/MeshFilter.h"
#include "../Components/Renderer/MeshRenderer.h"

/********************************************************************************************************
Constructor & Destructor
********************************************************************************************************/

Landscape::Landscape()
{
	_vertex = NULL;
	_name = "Landscape";
}

Landscape::Landscape(std::string name)
{
	_name = name;
	_vertex = NULL;
}

Landscape::~Landscape()
{
	delete(_vertex);
	delete(_index);
}

void Landscape::SetParameters(int nbVertexX, int nbVertexY, float sizeX, float sizeY, float smoothness)
{
	_nbVertexX = nbVertexX;
	_nbVertexY = nbVertexY;
	_sizeX = sizeX;
	_sizeY = sizeY;
	_smoothness = smoothness;

	_minHeight = - 2.0f;
	_maxHeight = 2.0f;
	SetVertexAndFaces();
	GenerateLandscape();
}

/********************************************************************************************************
Terrain Generation
********************************************************************************************************/


//Generate Landscape
void Landscape::GenerateLandscape()
{
	int nbIterations = _nbVertexX / 2;

	for(int i = 0; i < nbIterations; ++i)
	{
		Square(i + 1, _smoothness);
		Diamond(i + 1, _smoothness);
	}

	MeshFilter* mesh = GetComponent<MeshFilter>();
	//mesh->SetNormals(complex_cube_normales);
	mesh->SetVertex(_vertex, _nbVertex);
	mesh->Apply();

	AddComponent(new MeshRenderer());
}


void Landscape::Square(int depth, float smoothness)
{
	
	int nbIterations = (int)pow(4.0f, depth-1);//depth * depth;

	int step = (int)ceil(_nbVertexX / (1.0f * depth));
	int currentVertex = step / 2 + (step / 2) * _nbVertexX;
	int leftUpVertex = 0 * step;
	int rightUpVertex = step - 1;
	int rightDownVertex = rightUpVertex * 2 + (step - 1) * (_nbVertexX - 1);//_nbVertexX * step - 1;
	int leftDownVertex = step * (step - 1);

	
	for(int i = 0; i < nbIterations; ++i)
	{
		
		float randPos = Math::RandFloat(0, _maxHeight + abs(_minHeight)) - abs(_minHeight);
		float newPos = ComputeAveragePosition(leftUpVertex, rightUpVertex, rightDownVertex, leftDownVertex) + randPos;
		
		if(currentVertex >= 80)
			continue;

		_vertex[currentVertex * 3 + 1] = newPos;

		int decalLine = 0;
		int mod = (rightUpVertex + 1) % (_nbVertexX);

		if(mod == 0)
			//decalLine = 5;
			decalLine = rightDownVertex - rightUpVertex - _nbVertexX + 1;
			//decalLine += rightDownVertex - rightUpVertex - _nbVertexX + 1;

		currentVertex += step - 1 + decalLine;
		leftUpVertex = rightUpVertex + decalLine;
		rightUpVertex += step - 1 + decalLine;
		leftDownVertex = rightDownVertex + decalLine;
		rightDownVertex += step - 1 + decalLine;
		
	}
	
}


void Landscape::Diamond(int depth, float smoothness)
{
	
	int nbIterations = depth * depth;

	int step = (int)ceil(_nbVertexX / (1.0f * depth));
	int currentVertex = step / 2 + (step / 2) * _nbVertexX;
	int upVertex = currentVertex - (step / 2 * _nbVertexX);
	int downVertex = currentVertex + (step / 2 * _nbVertexX);
	int leftVertex = currentVertex - (step / 2);
	int rightVertex = currentVertex + (step / 2);
	
	
	
	for(int i = 0; i < nbIterations; ++i)
	{
		int tabVertex[4] = { upVertex, downVertex, leftVertex, rightVertex};

		for(int j = 0; j < 4; ++j)
		{
			int current = tabVertex[j];

			int stepMiddle = step;
			int up = current - (stepMiddle / 2 * _nbVertexX);
			int down = current + (stepMiddle / 2 * _nbVertexX);

			int lineCurrent = current / _nbVertexX;
			
			int left = current - (stepMiddle / 2);
			int right = current + (stepMiddle / 2);
			
			//Check if left & right is on same line
			if(left / _nbVertexX != lineCurrent)
				left = -1;

			if(right / _nbVertexX != lineCurrent)
				right = -1;

			float randPos = Math::RandFloat(0, _maxHeight + abs(_minHeight)) - abs(_minHeight);
			float newPos = ComputeAveragePosition(up, down, left, right) + randPos;

			_vertex[current * 3 + 1] = newPos;
		}
		
		
		int decalLine = 0;
		int mod = (rightVertex + 1) % (_nbVertexX);

		if(mod == 0)
			decalLine = downVertex - upVertex - _nbVertexX + 1;

		//decalLine = rightDownVertex - rightUpVertex - _nbVertexX + 1;

		int temp = currentVertex;
		currentVertex += step - 1 + decalLine;
		upVertex += currentVertex - temp;
		downVertex += currentVertex - temp;
		leftVertex += currentVertex - temp;
		rightVertex += currentVertex - temp;
		
	}
	
}


//Set vertex positions & faces
void Landscape::SetVertexAndFaces()
{
	//Get Initialize Values
	float startPosX = -_sizeX / 2.0f;
	float startPosY = -_sizeY / 2.0f;
	float stepX = _sizeX / (float)_nbVertexX;
	float stepY = _sizeY / (float)_nbVertexY;

	//Initialize array
	_nbVertex = _nbVertexX * _nbVertexY;
	_vertex = new float[_nbVertex * 3];

	//Set Position of Vertex
	int indexPos = 0;
	for(int i = 0; i < _nbVertexX; ++i)
	{
		for(int j = 0; j < _nbVertexY; ++j)
		{
			_vertex[indexPos++] = i * stepX + startPosX;
			_vertex[indexPos++] = 0;
			_vertex[indexPos++] = j * stepY + startPosY;
		}
	}

	//Compute Faces index
	std::vector<int> indexFaces = std::vector<int>();

	for(int i = 0; i + _nbVertexX < _nbVertex -1; i++){
		if ((i +1) % _nbVertexX != 0)
		{
			//First Face
			indexFaces.push_back(i);
			indexFaces.push_back(i + 1);
			indexFaces.push_back(i + _nbVertexX + 1);

			//Second Face
			indexFaces.push_back(i);
			indexFaces.push_back(i + _nbVertexX + 1);
			indexFaces.push_back(i + _nbVertexX);			
		}
	}

	int count = indexFaces.size();
	_index = new short[count];
	int numIndex = 0;	

	for(unsigned int i = 0; i < count; ++i)
	{
		_index[i] = indexFaces[i];
	}
	int nbIndex = count;

	AddComponent(new MeshFilter());
	MeshFilter* mesh = GetComponent<MeshFilter>();
	mesh->SetIndex(_index, nbIndex);
	//mesh->SetNormals(complex_cube_normales);
	mesh->SetVertex(_vertex, _nbVertex);
	mesh->Apply();

	AddComponent(new MeshRenderer());

}

//Compute average position of four vertex
float Landscape::ComputeAveragePosition(int leftUp, int rightUp, int rightDown, int leftDown)
{
	float sum = 0;
	int count = 0;

	if(IsVertexOnRange(leftUp))
	{
		sum += _vertex[leftUp * 3 + 1];
		count ++;
	}

	if(IsVertexOnRange(rightUp))
	{
		sum += _vertex[rightUp * 3 + 1];
		count ++;
	}

	if(IsVertexOnRange(rightDown))
	{
		sum += _vertex[rightDown * 3 + 1];
		count ++;
	}

	if(IsVertexOnRange(leftDown))
	{
		sum += _vertex[leftDown * 3 + 1];
		count ++;
	}
	
	return sum / (1.0f * count);
}

//Check if a vertex is on object or out of range
bool Landscape::IsVertexOnRange(int index)
{
	if(index > 0 && index < _nbVertex)
	{
		return true;
	}
	return false;
}